//go:build windows

package prolib

import (
	"bufio"
	"encoding/json"
	"fmt"
	"github.com/StackExchange/wmi"
	"github.com/gorpher/gowin32"
	"github.com/urfave/cli/v2"
	"os"
)

var (
	BuildTime string
	Version   = "1.0.0"
)

func Do() {
	app := cli.NewApp()
	app.Name = "idp-process-cli"
	app.Version = fmt.Sprintf("%s build:%s", Version, BuildTime)
	app.Usage = "为 idp 获取进程信息提供支持"
	app.Description = "获取进程信息包括：检查进程是否存在、列出进程列表、关闭进程 "
	app.Flags = []cli.Flag{
		&cli.StringFlag{
			Name:    "list",
			Aliases: []string{"l"},
			Usage:   "获取全部进程列表",
		},
		&cli.StringFlag{
			Name:    "name",
			Aliases: []string{"n"},
			Usage:   "检查进程",
		},
		&cli.StringFlag{
			Name:  "exePath",
			Usage: "检查进程时需要对执行文件路径进行校验,未指定该参数时只检查进程名称，不做执行文件路径校验",
		},
		&cli.IntFlag{
			Name:    "kill",
			Aliases: []string{"k"},
			Usage:   "关闭进程",
		},
		&cli.Int64Flag{
			Name:    "taskId",
			Aliases: []string{"id"},
			Usage:   "任务id",
		},
	}
	app.Action = func(ctx *cli.Context) error {
		var (
			pid    int
			bs     []byte
			ps     []ProcessInfo
			pName  string
			checkR RProcess
			taskId int64
			err    error
		)
		if ctx.IsSet("taskId") {
			taskId = ctx.Int64("taskId")
		}
		if ctx.IsSet("kill") {
			pid = ctx.Int("kill")
			return kill(uint(pid))
		}
		if ctx.IsSet("list") {
			if ps, err = getProcessList(); err != nil {
				return err
			}
			if bs, err = json.Marshal(ps); err != nil {
				return err
			}
		}
		if ctx.IsSet("name") {
			pName = ctx.String("name")
			exePath := ctx.String("exePath")
			if ps, err = checkProcess(pName); err != nil {
				return err
			}
			checkR = RProcess{
				TaskId:  taskId,
				Name:    pName,
				Path:    exePath,
				IsExist: false,
				Num:     0,
			}
			var num int
			if exePath == "" {
				checkR.Num = len(ps)
				checkR.IsExist = true
			} else {
				for _, v := range ps {
					if v.ExecutablePath == exePath {
						num++
					}
				}
				checkR.Num = num
				checkR.Path = ps[0].ExecutablePath
				checkR.IsExist = true
			}
			if bs, err = json.Marshal(checkR); err != nil {
				return err
			}
		}
		w := bufio.NewWriter(os.Stdout)
		w.Write(bs)
		w.WriteString("\n")
		return w.Flush()
	}
	if err := app.Run(os.Args); err != nil {
		fmt.Printf("命令行调用错误：%s ，请查看帮助文档", err.Error())
	}
}

// ProcessInfo 进程信息
type ProcessInfo struct {
	Caption        string // eg:chrome.exe , 标题
	CommandLine    string // eg:"C:\Program Files (x86)\Google\Chrome\Application\chrome.exe" --type=renderer  ，启动进程的命令行信息
	CreationDate   string // eg:20210819104620.286131+480 ,进程启动时间
	Description    string // eg:chrome.exe , 描述
	ExecutablePath string // eg:C:\Program Files (x86)\Google\Chrome\Application\chrome.exe ,执行文件路径
	Handle         string // eg:15000 ,进程ID
	HandleCount    uint32 // eg:379 ,该进程打开的文件句柄数
	Name           string // eg:chrome.exe ,进程名称
	Priority       uint32 // eg:8 ,进程优先级，范围0(低)~31(高)
	ProcessId      uint32 // eg:15000 ,进程ID
	SessionId      uint32 // eg:1 ,会话ID
	ThreadCount    uint32 // eg：16 ，该进程下的线程数
}

// getProcessList 获取全部进程列表
func getProcessList() (data []ProcessInfo, err error) {
	err = wmi.Query("select * from Win32_Process", &data)
	return
}

// checkProcess 检查进行是否存在
func checkProcess(processName string) (li []ProcessInfo, err error) {
	all, err := getProcessList()
	if err != nil {
		return
	}
	for _, v := range all {
		if v.Caption == processName {
			li = append(li, v)
		}
	}
	return
}

func kill(pid uint) error {
	err := gowin32.KillProcess(pid, 0)
	return err
}

type RProcess struct {
	TaskId  int64  `json:"taskId,string"`
	Name    string `json:"name"`
	Path    string `json:"path"`
	IsExist bool   `json:"isExist"`
	Num     int    `json:"num"`
}
